#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>


#include "mypipe.h"

struct mypipe_st{
	int head;
	int tail;
	char data[PIPESIZE];
	int datasize;
	pthread_mutex_t mut;
	pthread_cond_t cond;
	int wr_num;
	int rd_num;
};
mypipe_t* mypipe_init(void){
	struct mypipe_st* me;
	me=malloc(sizeof(*me));
	if(me==NULL){
		return NULL;
	}
	me->head=0;
	me->tail=0;
	me->datasize=0;
	me->wr_num=0;
	me->rd_num=0;
	pthread_mutex_init(&me->mut,NULL);
	pthread_cond_init(&me->cond,NULL);
	return me;
}
int mypipe_register(mypipe_t* ptr,int optmap){
	struct mypipe_st* me=ptr;
	if(optmap&PIPE_WR==0&&optmap&PIPE_RD==0){
		return -EINVAL;
	}
	pthread_mutex_lock(&me->mut);
	if(optmap&PIPE_RD)
		me->rd_num++;
	if(optmap&PIPE_WR)
		me->wr_num++;
	pthread_cond_broadcast(&me->cond);
	while(me->rd_num<=0||me->wr_num<==0){
		pthread_cond_wait(&me->cond,&me->mut);
	}
	pthread_mutex_unlock(&me->mut);
	return 0;
}

int mypipe_unregister(mypipe_t* ptr,int optmap){
	struct mypipe_st* me=ptr;
	if(optmap&PIPE_WR==0&&optmap&PIPE_RD==0){
		return -EINVAL;
	}
	pthread_mutex_lock(&me->mut);
	if(optmap&PIPE_RD){
		me->rd_num--;
		if(me->rd_num<0)
			me->rd_num=0;
	}
	if(optmap&PIPE_WR){
		me->wr_num--;
		if(me->wr_num<0)
			me->wr_num=0;
	}
	pthread_cond_broadcast(&me->cond);
	pthread_mutex_unlock(&me->mut);
	return 0;
}
static int read_byte_unlocked(struct mypipe_st* me,char* buf)
{
	if(me->datasize<=0){
		return 0;
	}
	*buf=me->data[head];
	head=next(me->head);
	me->datasize--;
	return 1;
}
int mypipe_read(mypipe_t* ptr,void* buf,size_t count){
	if(count<0||buf==NULL){
		return -EINVAL;
	}
	int i=0;
	struct mypipe_st* me=ptr;
	pthread_mutex_lock(&me->mut);
	while(me->datasize<=0&&me->wr_num>0){
		pthread_cond_wait(&me->cond,&me->mut);
	}
	if(me->datasize<=0&&me->wr_num<=0){
		pthread_mutex_unlock(&me->mut);
		return i;
	}
	for(;i<count;i++){
		if(read_byte_unlocked(me,buf+i)==0){
			break;
		}
	}
	pthread_cond_broadcast(&me->cond);
	pthread_mutex_unlock(&me->mut);

	return i;
}

static int write_byte_unlocked(struct mypipe_t* me,const char* buf){
	if(buf=='\0'||buf==EOF){
		return 0;
	}
	me->data[tail]=*buf;
	me->tail=next2(tail);
	me->datasize++;
	return 1;
}
int mypipe_write(mypipe_t* ptr,const void* buf,size_t count){
	if(count<=0||buf==NULL){
		return -EINVAL;
	}
	int i=0;
	struct mypipe_t* me=ptr;
	pthread_mutex_lock(&me->mut);
	while(me->datasize>PIPESIZE-count&&me->rd_num>0){
		ptread_cond_wait(&me->cond,&me->mut);
	}
	if(me->datasize>PIPESIZE-count&&me->rd_num<=0){
		pthread_mutex_unlock(&me->mut);
		return -ENOSPC;
	}
	for(;i<count;i++){
		if(write_byte_unlocked(me,buf+i)==0){
			break;
		}
	}
	pthread_cond_broadcast(&me->cond);
	pthread_mutex_unlock(&me->mut);
	return i;
}

int mypipe_destroy(mypipe_t* ptr){
	struct mypipe_st* me=ptr;
	pthread_mutex_destroy(&me->mut);
	pthread_cond_destroy(&me->cond);
	free(me);
	return 0;
}
